package com.good.shopping.product.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.good.shopping.product.entity.CategoryEntity;
import com.good.shopping.product.vo.Catelog2Vo;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 5 .分布式锁
 *       使用读写锁 优化 redis4 写法
 *
 *       优化方式：1.如果并发几率小 - 不用考虑这种，加上缓存过期时间，就可以解决，过一段时间，数据会自己更新缓存中的值
 *                2.可以使用cancl （阿里的中间件）使用这个更新缓存
 *                3.使用读写锁 （保证并发写、正常读）允许临时的脏数据
 *       数据实时性-就查询数据库，即使慢点。
 * @author shengren.yan
 * @create 2022-02-24
 */
public class redis5 {

    // redis
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    // 分布式锁
    @Autowired
    RedissonClient redisson;

    /**
     * 从数据库查询并封装数据::分布式锁
     *  优化 在 redis5 使用读写锁
     *    使用读锁
     * @return
     */
    public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {
        // 1 创建读锁
        RReadWriteLock lock = redisson.getReadWriteLock("catalogJSON-lock");
        RLock rLock = lock.readLock();
        rLock.lock();
        System.out.println("获取分布式锁成功...");
        Map<String, List<Catelog2Vo>> dataFromDb = null;
        try {
            //加锁成功...执行业务
            dataFromDb = getDataFromDb();
        } finally {
            // 释放锁
            rLock.unlock();
        }

        return dataFromDb;
    }


    // 查询数据库
    private Map<String, List<Catelog2Vo>> getDataFromDb() {
        //得到锁以后，我们应该再去缓存中确定一次，如果没有才需要继续查询  catalogJson 缓存数据
        String catalogJson = stringRedisTemplate.opsForValue().get("catalogJson");
        if (!StringUtils.isEmpty(catalogJson)) {
            //缓存不为空直接返回
            Map<String, List<Catelog2Vo>> result = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catelog2Vo>>>() {
            });
            return result;
        }

        System.out.println("查询了数据库");

        //1、查出所有分类

        // 封装数据 -- 模拟查询数据库
        Map<String, List<Catelog2Vo>> parentCid = null;

        //3、将查到的数据放入缓存,将对象转为json  catalogJson 缓存数据
        String valueJson = JSON.toJSONString(parentCid);
        stringRedisTemplate.opsForValue().set("catalogJson", valueJson, 1, TimeUnit.DAYS);

        return parentCid;
    }


    // 写锁
    public void updateCascade(CategoryEntity category) {

        RReadWriteLock readWriteLock = redisson.getReadWriteLock("catalogJSON-lock");
        //创建写锁
        RLock rLock = readWriteLock.writeLock();

        try {
            rLock.lock();
            System.out.println("修改 的SQL语句");
            System.out.println(" 其他业务代码 ");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }

        //同时修改缓存中的数据
        //删除缓存,等待下一次主动查询进行更新
        // redisTemplate.delete("catalogJSON"); // 失效模式

    }

}
